/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is Forte for Java, Community Edition. The Initial
* Developer of the Original Code is Sun Microsystems, Inc. Portions
* Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
*/
package org.netbeans.modules.vcs;
import java.awt.*;
import java.io.*;
import java.util.*;
import java.beans.*;
import java.text.*;
import javax.swing.*;
import org.openide.util.actions.*;
import org.openide.util.NbBundle;
import org.openide.*;
import org.netbeans.modules.vcs.cmdline.*;
import org.netbeans.modules.vcs.util.*;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileSystem;
import org.openide.filesystems.FileSystem.Status;
import org.openide.filesystems.FileUtil;
import org.openide.filesystems.FileStateInvalidException;
import org.openide.filesystems.AbstractFileSystem;
import org.openide.filesystems.DefaultAttributes;
import org.openide.filesystems.FileStatusEvent;
/** Generic VCS filesystem.
*
* @author Michal Fadljevic
*/
//-------------------------------------------
public abstract class VcsFileSystem extends AbstractFileSystem implements AbstractFileSystem.List, AbstractFileSystem.Info, AbstractFileSystem.Change, FileSystem.Status, Serializable {
private Debug E=new Debug("VcsFileSystem", true); // NOI18N
private Debug D=E;
/**
* @associates UserCommand
*/
private transient Hashtable commandsByName=null;
protected static final int REFRESH_TIME = 0;
protected int refreshTimeToSet = REFRESH_TIME;
private static final String LOCAL_FILES_ADD_VAR = "SHOWLOCALFILES"; // NOI18N
private static final String VAR_TRUE = "true"; // NOI18N
private static final String VAR_FALSE = "false"; // NOI18N
private static final String LOCK_FILES_ON = "LOCKFILES"; // NOI18N
private static final String PROMPT_FOR_LOCK_ON = "PROMPTFORLOCK"; // NOI18N
/**
* The name of the variable for which we get user input.
*/
private static final String PROMPT_FOR = "PROMPT_FOR"; // NOI18N
/**
* The name of the variable for which we get user to set true or false.
*/
private static final String ASK_FOR = "ASK_FOR"; // NOI18N
private static int last_refreshTime = REFRESH_TIME;
private static File last_rootFile = new File (System.getProperty("user.home")); // NOI18N
private static boolean last_useUnixShell = false;
/** root file */
private File rootFile = last_rootFile; // NOI18N
private boolean useUnixShell = last_useUnixShell;
/** is read only */
private boolean readOnly;
/**
* @associates VcsConfigVariable
*/
protected Hashtable variablesByName = new Hashtable ();
private boolean lockFilesOn = false;
private boolean promptForLockOn = true;
private volatile boolean promptForLockResult = false;
private boolean debug=true;
private String config="Empty"; // NOI18N
/** user variables Vector<String> 'name=value' */
private Vector variables=new Vector(10);
private transient String password=null;
/** advanced confgiguration */
private Object advanced=null;
protected transient VcsCache cache=null;
private long cacheId=0;
private static transient String CACHE_ROOT="vcs/cache"; // NOI18N
// private static transient long CACHE_LAST_ID=0;
private transient VcsAction action=null;
private transient ErrorCommandDialog errorDialog = null;
private transient volatile boolean lastCommandState = true;
private transient volatile boolean lastCommandFinished = true;
/**
* @associates String
*/
private transient Vector unimportantNames;
protected boolean ready=false;
private boolean askIfDownloadRecursively = true;
/**
* @associates Integer
*/
private volatile Hashtable numDoAutoRefreshes = new Hashtable();
/**
* Whether to prompt the user for variables for each selected file. Value of this variable
* willl be the default value in the VariableInputDialog and changing the value there will
* change the value of this variable.
*/
private boolean promptForVarsForEachFile = false;
public boolean isLockFilesOn () { return lockFilesOn; }
public void setLockFilesOn (boolean lock) { lockFilesOn = lock; }
public boolean isPromptForLockOn () { return promptForLockOn; }
public void setPromptForLockOn (boolean prompt) { promptForLockOn = prompt; }
public boolean getAskIfDownloadRecursively () { return askIfDownloadRecursively; }
public void setAskIfDownloadRecursively (boolean ask) { askIfDownloadRecursively = ask; }
public boolean isUseUnixShell () { return useUnixShell; }
protected void setUseUnixShell (boolean unixShell) {
useUnixShell = unixShell;
last_useUnixShell = unixShell;
}
/**
* Get whether to perform the auto-refresh in the given directory path.
* @param path The given directory path
*/
public synchronized boolean getDoAutoRefresh(String path) {
synchronized (numDoAutoRefreshes) {
D.deb("getDoAutoRefresh("+path+") ..."); // NOI18N
int numDoAutoRefresh = getNumDoAutoRefresh(path);
if (numDoAutoRefresh > 0) {
numDoAutoRefresh--;
if (numDoAutoRefresh > 0) setNumDoAutoRefresh(numDoAutoRefresh, path);
else removeNumDoAutoRefresh(path);
D.deb(" return "+(numDoAutoRefresh == 0)); // NOI18N
return (numDoAutoRefresh == 0);
} else {D.deb(" return true"); return true;} // nothing known about that path, but refresh requested. // NOI18N
}
}
/**
* Set how many times I call a command after which the auto-refresh is executed in the given path.
* @param numDoAutoRefresh The number of auto-refreshes
* @param path The given directory path
*/
public synchronized void setNumDoAutoRefresh(int numDoAutoRefresh, String path) {
synchronized (numDoAutoRefreshes) {
D.deb("setNumDoAutoRefresh("+numDoAutoRefresh+", "+path+")"); // NOI18N
numDoAutoRefreshes.put(path, new Integer(numDoAutoRefresh));
}
}
/**
* Get the number of command calls after which perform the auto-refresh command in the given path.
* @param path The given path
*/
public synchronized int getNumDoAutoRefresh(String path) {
synchronized (numDoAutoRefreshes) {
Integer numDoAutoRefreshObj = (Integer) numDoAutoRefreshes.get(path);
int numDoAutoRefresh = 0;
if (numDoAutoRefreshObj != null) {
numDoAutoRefresh = numDoAutoRefreshObj.intValue();
}
D.deb("getNumDoAutoRefresh("+path+") = "+numDoAutoRefresh); // NOI18N
return numDoAutoRefresh;
}
}
/**
* Remove the number of command calls after which perform the auto-refresh command in the given path.
* @param path The given path
*/
public synchronized void removeNumDoAutoRefresh(String path) {
synchronized (numDoAutoRefreshes) {
D.deb("removeNumDoAutoRefresh("+path+")"); // NOI18N
numDoAutoRefreshes.remove(path);
}
}
public boolean getLastCommandState () { return lastCommandState; }
public void setLastCommandState (boolean lastCommandState) { this.lastCommandState = lastCommandState; }
public boolean getLastCommandFinished () { return lastCommandFinished; }
public void setLastCommandFinished (boolean lastCommandFinished) { this.lastCommandFinished = lastCommandFinished; }
//-------------------------------------------
public String getConfigRoot(){
return ""; // NOI18N
}
public FileObject getConfigRootFO(){
return null; // NOI18N
}
/*
* Mark the file as being unimportant.
* @param name the file name
*/
public void markUnimportant(String name) {
D.deb("==== unimportant("+name+") ====");
if (!unimportantNames.contains(name)) unimportantNames.addElement(name);
}
public boolean isImportant(String name) {
D.deb("isImportant("+name+")");
D.deb("unimportantNames = "+unimportantNames);
D.deb("contains() = "+unimportantNames.contains(name));;
return !unimportantNames.contains(name);
}
/**
* Perform refresh of status information on all children of a directory
* @param path the directory path
* @param recursivey whether to refresh recursively
*/
public void statusChanged (String path, boolean recursively) {
//D.deb("statusChanged("+path+")"); // NOI18N
FileObject fo = findResource(path);
if (fo == null) return;
//D.deb("I have root = "+fo.getName()); // NOI18N
Enumeration enum = fo.getChildren(recursively);
HashSet hs = new HashSet();
while(enum.hasMoreElements()) {
fo = (FileObject) enum.nextElement();
hs.add(fo);
//D.deb("Added "+fo.getName()+" fileObject to update status"+fo.getName()); // NOI18N
}
Set s = Collections.synchronizedSet(hs);
fireFileStatusChanged (new FileStatusEvent(this, s, false, true));
}
public void setCustomRefreshTime (int time) {
if (isValid ()) {
D.deb("Filesystem valid, setting the refresh time to "+time); // NOI18N
setRefreshTime (time);
} else {
D.deb("Filesystem not valid yet for refresh time "+time); // NOI18N
refreshTimeToSet = time;
}
last_refreshTime = time;
}
public int getCustomRefreshTime () {
if (isValid ()) {
D.deb("Filesystem valid, getting the refresh time "+getRefreshTime ()); // NOI18N
return getRefreshTime ();
} else return refreshTimeToSet;
}
//-------------------------------------------
public void setConfig(String label){
this.config=label;
}
//-------------------------------------------
public String getConfig(){
return config;
}
//-------------------------------------------
public void debugClear(){
if( getDebug() ){
try{
TopManager.getDefault().getStdOut().reset();
}catch (IOException e){}
}
}
//-------------------------------------------
public void debug(String msg){
if( getDebug() ){
TopManager.getDefault().getStdOut().println(msg);
}
}
//-------------------------------------------
public void setImportant(boolean important){
//D.deb("setImportant("+important+")"); // NOI18N
}
//-------------------------------------------
public VcsCache getCache(){
return cache;
}
//-------------------------------------------
public void setCache(VcsCache cache) {
this.cache = cache;
}
public abstract VcsFactory getVcsFactory ();
//-------------------------------------------
private void createDir(String path){
File dir=new File(path);
if( dir.isDirectory() ){
return ;
}
if( dir.mkdirs()==false ){
E.err(g("MSG_UnableToCreateDirectory", path)); // NOI18N
debug(g("MSG_UnableToCreateDirectory", path)); // NOI18N
}
}
//-------------------------------------------
protected String createNewCacheDir(){
String dir;
if(cacheId==0) {
do {
cacheId = 10000 * (1 + Math.round (Math.random () * 8)) + Math.round (Math.random () * 1000);
} while (new File(CACHE_ROOT+File.separator+cacheId).isDirectory ());
}
dir = CACHE_ROOT+File.separator+cacheId;
createDir (dir);
return dir;
}
//-------------------------------------------
protected void init(){
D.deb ("init()"); // NOI18N
unimportantNames = new Vector();
CACHE_ROOT=System.getProperty("netbeans.user")+File.separator+
"system"+File.separator+"vcs"+File.separator+"cache"; // NOI18N
cache=new VcsCache(this, createNewCacheDir ());
errorDialog = new ErrorCommandDialog(null, new JFrame(), false);
try {
setInitRootDirectory(rootFile);
} catch (PropertyVetoException e) {
// Could not set root directory
} catch (IOException e) {
// Could not set root directory
}
}
static final long serialVersionUID =8108342718973310275L;
//-------------------------------------------
public VcsFileSystem() {
D.deb("VcsFileSystem()"); // NOI18N
info = this;
change = this;
DefaultAttributes a = new DefaultAttributes (info, change, this);
attr = a;
list = a;
setRefreshTime (last_refreshTime);
refreshTimeToSet = last_refreshTime;
init();
D.deb("constructor done.");
}
public ErrorCommandDialog getErrorDialog() {
return errorDialog;
}
public void setErrorDialog(ErrorCommandDialog errDlg) {
errorDialog = errDlg;
}
//-------------------------------------------
public long getCacheId(){
return cacheId;
}
//-------------------------------------------
private void readObject(ObjectInputStream in) throws
//D.deb("readObject() - restoring bean"); // NOI18N
ClassNotFoundException, IOException, NotActiveException{
// cache is transient
boolean localFilesOn = in.readBoolean ();
in.defaultReadObject();
init();
cache.setLocalFilesAdd (localFilesOn);
}
//-------------------------------------------
private void writeObject(ObjectOutputStream out) throws IOException {
//D.deb("writeObject() - saving bean"); // NOI18N
// cache is transient
out.writeBoolean (cache.isLocalFilesAdd ());
out.defaultWriteObject();
}
//-------------------------------------------
public void setDebug(boolean debug){
this.debug=debug;
}
//-------------------------------------------
public boolean getDebug(){
return debug;
}
//-------------------------------------------
public Vector getVariables(){
return variables;
}
//-------------------------------------------
public void setVariables(Vector variables){
//D.deb ("setVariables()"); // NOI18N
boolean containsCd = false;
String cdValue = System.getProperty ("os.name").equals ("Windows NT") ? "cd /D" : "cd";
int len = variables.size ();
VcsConfigVariable var;
for(int i=0; i<len; i++){
var = (VcsConfigVariable) variables.get (i);
if(var.getName ().equalsIgnoreCase (LOCAL_FILES_ADD_VAR)) {
if(var.getValue ().equalsIgnoreCase (VAR_TRUE)) {
cache.setLocalFilesAdd (true);
}
if(var.getValue ().equalsIgnoreCase (VAR_FALSE)) {
cache.setLocalFilesAdd (false);
}
}
if(var.getName ().equalsIgnoreCase (LOCK_FILES_ON)) {
if(var.getValue ().equalsIgnoreCase (VAR_TRUE)) {
setLockFilesOn (true);
}
if(var.getValue ().equalsIgnoreCase (VAR_FALSE)) {
setLockFilesOn (false);
}
}
if(var.getName ().equalsIgnoreCase (PROMPT_FOR_LOCK_ON)) {
if(var.getValue ().equalsIgnoreCase (VAR_TRUE)) {
setPromptForLockOn (true);
}
if(var.getValue ().equalsIgnoreCase (VAR_FALSE)) {
setPromptForLockOn (false);
}
}
if(var.getName ().equals ("CD")) { // NOI18N
//var.setValue (cdValue); <- I don't want to change the value if it is set !!
containsCd = true;
}
}
if( variables.equals(this.variables) ){
return ;
}
if (!containsCd) {
variables.add (new VcsConfigVariable ("CD", "cd", cdValue, false, false, false, "", 0)); // NOI18N
}
Vector old=this.variables;
this.variables=variables;
variablesByName.clear ();
for (int i=0, n=variables.size (); i<n; i++) {
var = (VcsConfigVariable) variables.get (i);
variablesByName.put (var.getName (), var);
}
firePropertyChange("variables", old, variables); // NOI18N
}
public static String substractRootDir(String rDir, String module) {
if (module == null || module.length() == 0) return rDir;
String m;
if (module.charAt(module.length() - 1) == File.separatorChar)
m = module.substring(0, module.length() - 1);
else
m = module.substring(0);
String rDirSlashes;
boolean chRDir = false;
if (File.separatorChar != '/' && rDir.indexOf(File.separatorChar) > 0) {
rDirSlashes = rDir.replace(File.separatorChar, '/');
chRDir = true;
} else rDirSlashes = rDir;
String moduleSlashes;
if (File.separatorChar != '/' && m.indexOf(File.separatorChar) > 0) {
moduleSlashes = m.replace(File.separatorChar, '/');
} else moduleSlashes = m;
int i = rDirSlashes.lastIndexOf(moduleSlashes);
if (i <= 0) return rDir;
if (chRDir) return rDir.substring(0, i-1).replace('/', File.separatorChar);
else return rDir.substring(0, i-1); // I have to remove the slash also.
}
//-------------------------------------------
public Hashtable getVariablesAsHashtable(){
int len=getVariables().size();
Hashtable result=new Hashtable(len+5);
for(int i=0; i<len; i++) {
VcsConfigVariable var = (VcsConfigVariable) getVariables().elementAt (i);
result.put(var.getName (), var.getValue ());
}
result.put("netbeans.home",System.getProperty("netbeans.home"));
result.put("netbeans.user",System.getProperty("netbeans.user"));
result.put("java.home",System.getProperty("java.home"));
String osName=System.getProperty("os.name");
result.put("classpath.separator", (osName.indexOf("Win")<0 ? ":":";" )); // NOI18N
result.put("path.separator", ""+File.separator); // NOI18N
if(result.get("PS")==null) { // NOI18N
result.put("PS", ""+File.separator); // NOI18N
}
String rootDir = getRootDirectory().toString();
String module = (String) result.get("MODULE"); // NOI18N
//if (osName.indexOf("Win") >= 0) // NOI18N
//module=module.replace('\\','/');
result.put("ROOTDIR", substractRootDir(rootDir, module)); // NOI18N
return result;
}
//-------------------------------------------
public void setPassword(String password){
this.password=password;
}
//-------------------------------------------
public String getPassword(){
return password;
}
/**
* Find out what to prompt for the user for before running the command.
* @param exec The command to exec
* @param vars The variables to use
* @return The array of variable labels for the user to input, one for each variable
*/
private String[] needPromptFor(String exec, Hashtable vars) {
Vector results = new Vector();
String search = "${"+PROMPT_FOR+"(";
int pos = 0;
int index;
while((index = exec.indexOf(search, pos)) >= 0) {
index += search.length();
int index2 = exec.indexOf(")", index);
if (index2 < 0) break;
String str = exec.substring(index, index2);
results.addElement(str);
pos = index2;
}
return (String[]) results.toArray(new String[0]);
}
/**
* Find out what to ask the user for before running the command.
* @param exec The command to exec
* @param vars The variables to use
* @return The array of questions for the user, one for each variable
*/
private String[] needAskFor(String exec, Hashtable vars) {
Vector results = new Vector();
String search = /*"${"+*/ASK_FOR+"("; // to be able to put this to conditional expression
int pos = 0;
int index;
while((index = exec.indexOf(search, pos)) >= 0) {
index += search.length();
int index2 = exec.indexOf(")", index);
if (index2 < 0) break;
String str = exec.substring(index, index2);
results.addElement(str);
pos = index2;
}
return (String[]) results.toArray(new String[0]);
}
//-------------------------------------------
private boolean needPromptForPR(String name, String exec, Hashtable vars){
//D.deb("needPromptFor('"+name+"','"+exec+"')"); // NOI18N
boolean result=false;
String oldPassword=(String)vars.get("PASSWORD"); vars.put("PASSWORD",""); // NOI18N
String oldReason=(String)vars.get("REASON"); vars.put("REASON",""); // NOI18N
String test="variable_must_be_prompt_for"; // NOI18N
vars.put(name,test);
Variables v=new Variables();
String s=v.expand(vars,exec, false);
result= ( s.indexOf(test)>=0 ) ? true : false ;
if( oldPassword!=null ){ vars.put("PASSWORD",oldPassword); } // NOI18N
if( oldReason!=null ){ vars.put("REASON",oldReason); } // NOI18N
return result ;
}
/**
* Ask the user for the value of some variables.
* @param exec the command to execute
* @param vars the variables
* @param forEachFile whether to ask for these variables for each file being processed
* @return true if all variables were entered, false otherways
*/
public boolean promptForVariables(String exec, Hashtable vars, boolean[] forEachFile){
if( needPromptForPR("PASSWORD",exec,vars) ){ // NOI18N
String password=getPassword();
if(password==null){
password = ""; // NOI18N
NotifyDescriptorInputPassword nd = new NotifyDescriptorInputPassword (g("MSG_Password"), g("MSG_Password")); // NOI18N
if(NotifyDescriptor.OK_OPTION.equals (TopManager.getDefault ().notify (nd))) {
password = nd.getInputText ();
} else return false;
setPassword(password);
}
vars.put("PASSWORD",password); // NOI18N
/* Do not change forEachFile, if the command is successful it will not ask any more */
}
if (forEachFile == null || forEachFile[0] == true) {
if( needPromptForPR("REASON",exec,vars) ){ // NOI18N
String reason=""; // NOI18N
String file = (String) vars.get("FILE"); // NOI18N
/*
NotifyDescriptor.InputLine nd;
if (file != null)
nd = new NotifyDescriptor.InputLine (g("MSG_Reason"), g("MSG_ReasonFor", file)); // NOI18N
else
nd = new NotifyDescriptor.InputLine (g("MSG_Reason"), g("MSG_Reason")); // NOI18N
if(NotifyDescriptor.OK_OPTION.equals (TopManager.getDefault ().notify (nd))) {
reason = nd.getInputText ();
} else return false;
*/
String[] prompt = new String[1];
if (file != null) {
prompt[0] = new String(g("MSG_ReasonFor", file));
} else {
prompt[0] = new String(g("MSG_Reason"));
}
VariableInputDialog dlg = new VariableInputDialog(new java.awt.Frame(), true);
dlg.setVarPromptLabels(prompt);
if (forEachFile == null) dlg.showPromptEach(false);
else dlg.setPromptEach(promptForVarsForEachFile);
if (dlg.showDialog()) {
String[] values = dlg.getVarPromptValues();
reason = values[0];
if (forEachFile != null) {
forEachFile[0] = dlg.getPromptForEachFile();
promptForVarsForEachFile = forEachFile[0];
}
} else return false;
vars.put("REASON", MiscStuff.msg2CmdlineStr(reason, isUseUnixShell())); // NOI18N
}
String[] prompt = needPromptFor(exec, vars);
String[] ask = needAskFor(exec, vars);
if (prompt != null && prompt.length > 0 || ask != null && ask.length > 0) {
VariableInputDialog dlg = new VariableInputDialog(new java.awt.Frame(), true);
dlg.setVarPromptLabels(prompt);
dlg.setVarAskLabels(ask);
if (forEachFile == null) dlg.showPromptEach(false);
else dlg.setPromptEach(promptForVarsForEachFile);
if (dlg.showDialog()) {
String[] values = dlg.getVarPromptValues();
for(int i = 0; i < prompt.length; i++) {
vars.put(PROMPT_FOR+"("+prompt[i]+")", MiscStuff.msg2CmdlineStr(values[i], isUseUnixShell()));
}
values = dlg.getVarAskValues();
for(int i = 0; i < ask.length; i++) {
vars.put(ASK_FOR+"("+ask[i]+")", values[i]);
}
if (forEachFile != null) {
forEachFile[0] = dlg.getPromptForEachFile();
promptForVarsForEachFile = forEachFile[0];
}
} else return false;
}
}
return true;
}
protected void warnDirectoriesDoNotExists() {
D.deb("warnDirectoriesDoNotExists()");
Hashtable vars = getVariablesAsHashtable();
String module = (String) vars.get("MODULE");
if (module == null) module = "";
String rootDir = substractRootDir(getRootDirectory().toString(), module);
File root = new File(rootDir);
D.deb("RootDirectory = "+rootDir);
if( root == null || !root.isDirectory() ){
//E.err("not directory "+root); // NOI18N
D.deb("NOT DIRECTORY: "+root);
final String badDir = root.toString();
javax.swing.SwingUtilities.invokeLater(new Runnable () {
public void run () {
TopManager.getDefault ().notify (new NotifyDescriptor.Message(MessageFormat.format (org.openide.util.NbBundle.getBundle(VcsFileSystem.class).getString("Filesystem.notRootDirectory"), new Object[] { badDir } )));
}
});
return ;
}
File moduleDir = new File(root, module);
D.deb("moduleDir = "+moduleDir);
if( moduleDir == null || !moduleDir.isDirectory() ){
D.deb("NOT DIRECTORY: "+moduleDir);
final String badDir = module;
javax.swing.SwingUtilities.invokeLater(new Runnable () {
public void run () {
TopManager.getDefault ().notify (new NotifyDescriptor.Message(MessageFormat.format (org.openide.util.NbBundle.getBundle(VcsFileSystem.class).getString("Filesystem.notModuleDirectory"), new Object[] { badDir } )));
}
});
}
}
//-------------------------------------------
public FileSystem.Status getStatus(){
return this;
}
//-------------------------------------------
public Image annotateIcon(Image icon, int iconType, Set files) {
//D.deb("annotateIcon()"); // NOI18N
return icon;
}
//-------------------------------------------
public String annotateName(String name, Set files) {
String result=name;
String fullName=""; // NOI18N
String fileName=""; // NOI18N
Object[] oo=files.toArray();
int len=oo.length;
if( len==0 || name.indexOf(getRootDirectory().toString())>=0){
return result;
}
if( len==1 ){
FileObject ff=(FileObject)oo[0];
fullName=ff.getPackageNameExt('/','.');
fileName=MiscStuff.getFileNamePart(fullName);
String status=cache.getFileStatus(fullName).trim();
//D.deb("name = "+fullName+": status = "+status);
if( status.length()>0 ){
result=name+" ["+status+"]"; // NOI18N
}
String locker = cache.getFileLocker(fullName);
//D.deb("locker = '"+locker+"'");
if (locker != null && locker.length() > 0) {
result += " ("+locker+")"; // NOI18N
}
}
else{
Vector/*<VcsFile>*/ importantFiles=getImportantFiles(oo);
String status=cache.getStatus(importantFiles).trim();
//D.deb("status = "+status);
if( status.length()>0 ){
result=name+" ["+status+"]"; // NOI18N
}
String locker = cache.getLocker(importantFiles);
//D.deb("locker = '"+locker+"'");
if (locker != null && locker.length() > 0) {
result += " ("+locker+")"; // NOI18N
}
}
D.deb("annotateName("+name+") -> result='"+result+"'"); // NOI18N
return result;
}
//-------------------------------------------
private Vector/*VcsFile*/ getImportantFiles(Object[] oo){
//D.deb("getImportantFiles()"); // NOI18N
Vector result=new Vector(3);
int len=oo.length;
for(int i=0;i<len;i++){
FileObject ff=(FileObject)oo[i];
String fullName=ff.getPackageNameExt('/','.');
String fileName=MiscStuff.getFileNamePart(fullName);
VcsFile file=cache.getFile(fullName);
if( file==null ){
D.deb("no such file '"+fullName+"'"); // NOI18N
continue ;
}
//D.deb("fileName="+fileName); // NOI18N
//if( file.isImportant() ){ // TODO Change this line !!!
if( fileName.indexOf(".class")<0 ){ // NOI18N
result.addElement(file);
}
}
return result;
}
//-------------------------------------------
public SystemAction[] getActions(){
//D.deb("getActions()"); // NOI18N
if( action==null ){
action=getVcsFactory ().getVcsAction(this);
}
SystemAction [] actions=new SystemAction[1];
actions[0]=action;
return actions;
}
public void setValidFS(boolean v) {
boolean valid = isValid();
D.deb("Filesystem is "+((valid) ? "":"not ")+"valid.");
if (v != valid) {
D.deb("setting valid = "+v);
firePropertyChange (org.openide.filesystems.FileSystem.PROP_VALID,
new Boolean (!v), new Boolean (v));
}
D.deb("Filesystem is "+((isValid()) ? "":"not ")+"valid.");
}
//-------------------------------------------
/* Human presentable name */
public String getDisplayName() {
//D.deb("getDisplayName() isValid="+isValid()); // NOI18N
/*
if(!isValid())
return g("LAB_FileSystemInvalid", rootFile.toString ()); // NOI18N
else
*/
return g("LAB_FileSystemValid", rootFile.toString ()); // NOI18N
}
/**
* Set the root directory of the filesystem to the parameter passed.
* @param r file to set root to
* @exception PropertyVetoException if the value if vetoed by someone else (usually
* by the {@link org.openide.filesystems.Repository Repository})
* @exception IOException if the root does not exists or some other error occured
*/
private void setInitRootDirectory(File r) throws PropertyVetoException, IOException {
Hashtable vars = getVariablesAsHashtable();
String module = (String) vars.get("MODULE");
if (module == null) module = "";
String root = r.getCanonicalPath();
if (module.length() > 0) {
int i = root.indexOf(module);
if (i > 0) root = root.substring(0, i - 1);
}
r = new File(root);
setRootDirectory(r);
}
//-------------------------------------------
/** Set the root directory of the file system. It adds the module name to the parameter.
* @param r file to set root to plus module name
* @exception PropertyVetoException if the value if vetoed by someone else (usually
* by the {@link org.openide.filesystems.Repository Repository})
* @exception IOException if the root does not exists or some other error occured
*/
public synchronized void setRootDirectory (File r) throws PropertyVetoException, IOException {
//D.deb("setRootDirectory("+r+")"); // NOI18N
if (/*!r.exists() ||*/ r.isFile ()) {
throw new IOException(g("EXC_RootNotExist", r.toString ())); // NOI18N
}
Hashtable vars = getVariablesAsHashtable();
String module = (String) vars.get("MODULE");
if (module == null) module = "";
File root = new File(r, module);
String name = computeSystemName (root);
/* Ignoring other filesystems' names => it is possible to mount VCS filesystem with the same name.
Enumeration en = TopManager.getDefault ().getRepository ().fileSystems ();
while (en.hasMoreElements ()) {
FileSystem fs = (FileSystem) en.nextElement ();
if (fs.getSystemName ()==name) {
// NotifyDescriptor.Exception nd = new NotifyDescriptor.Exception (
throw new PropertyVetoException ("Directory already mounted", // NOI18N
new PropertyChangeEvent (this, "RootDirectory", getSystemName (), name)); // NOI18N
// TopManager.getDefault ().notify (nd);
}
}
*/
D.deb("Setting system name '"+name+"'"); // NOI18N
setSystemName(name);
rootFile = root;
last_rootFile = new File(""+root);
ready=true ;
firePropertyChange("root", null, refreshRoot ()); // NOI18N
}
//-------------------------------------------
public void setRootFile(File rootFile) {
this.rootFile = rootFile;
}
//-------------------------------------------
/** Get the root directory of the file system.
* @return root directory
*/
public File getRootDirectory () {
return rootFile;
}
//-------------------------------------------
/** Set whether the file system should be read only.
* @param flag <code>true</code> if it should
*/
public void setReadOnly(boolean flag) {
D.deb("setReadOnly("+flag+")"); // NOI18N
if (flag != readOnly) {
readOnly = flag;
firePropertyChange (PROP_READ_ONLY, new Boolean (!flag), new Boolean (flag));
}
}
//-------------------------------------------
/* Test whether file system is read only.
* @return <true> if file system is read only
*/
public boolean isReadOnly() {
//D.deb("isReadOnly() ->"+readOnly); // NOI18N
return readOnly;
}
//-------------------------------------------
/** Prepare environment by adding the root directory of the file system to the class path.
* @param environment the environment to add to
*/
public void prepareEnvironment(FileSystem.Environment environment) {
D.deb("prepareEnvironment() ->"+rootFile.toString()); // NOI18N
environment.addClassPath(rootFile.toString ());
}
//-------------------------------------------
/** Compute the system name of this file system for a given root directory.
* <P>
* The default implementation simply returns the filename separated by slashes.
* @see FileSystem#setSystemName
* @param rootFile root directory for the filesystem
* @return system name for the filesystem
*/
protected String computeSystemName (File rootFile) {
D.deb("computeSystemName() ->"+rootFile.toString ().replace(File.separatorChar, '/') ); // NOI18N
return rootFile.toString ().replace(File.separatorChar, '/');
}
//-------------------------------------------
/** Creates file for given string name.
* @param name the name
* @return the file
*/
public File getFile (String name) {
return new File (rootFile, name);
}
//-------------------------------------------
//
// List
//
//-------------------------------------------
/* Scans children for given name
*/
public String[] children (String name) {
D.deb("children('"+name+"')"); // NOI18N
String[] vcsFiles=null;
String[] files=null;
if( !ready ){
D.deb("not ready"); // NOI18N
return new String[0];
}
if( cache.isDir(name) ){
vcsFiles=cache.getFilesAndSubdirs(name);
D.deb("vcsFiles="+MiscStuff.arrayToString(vcsFiles)); // NOI18N
String p=""; // NOI18N
try{
p=rootFile.getCanonicalPath();
}
catch (IOException e){
E.err(e,"getCanonicalPath() failed"); // NOI18N
}
files=cache.dirsFirst(p+File.separator+name,vcsFiles);
D.deb("files="+MiscStuff.arrayToString(files)); // NOI18N
return files;
}
return new String[0];
}
// create local folder for existing VCS folder that is missing
private void checkLocalFolder (String name) throws java.io.IOException {
StringTokenizer st = new java.util.StringTokenizer (name, "/"); // NOI18N
String dir = null;
while(st.hasMoreElements()) {
dir = dir==null ? (String) st.nextElement () : dir + "/" + (String) st.nextElement (); // NOI18N
File f = getFile (dir);
if(f.exists ()) continue;
Object[] errorParams = new Object[] {
f.getName (),
getDisplayName (),
f.toString ()
};
boolean b = f.mkdir();
if (!b) {
throw new IOException(MessageFormat.format (g("EXC_CannotCreateF"), errorParams)); // NOI18N
}
D.deb ("local dir created='"+dir+"'"); // NOI18N
}
}
//-------------------------------------------
//
// Change
//
/* Creates new folder named name.
* @param name name of folder
* @throws IOException if operation fails
*/
public void createFolder (String name) throws java.io.IOException {
D.deb("createFolder('"+name+"')"); // NOI18N
if( name.startsWith("/") ){ // NOI18N
// Jarda TODO
name=name.substring(1);
D.deb("corrected name='"+name+"'"); // NOI18N
}
File f = getFile (name);
Object[] errorParams = new Object[] {
f.getName (),
getDisplayName (),
f.toString ()
};
if (name.equals ("")) { // NOI18N
throw new IOException(MessageFormat.format (g("EXC_CannotCreateF"), errorParams)); // NOI18N
}
if (f.exists()) {
throw new IOException(MessageFormat.format (g("EXC_FolderAlreadyExist"), errorParams)); // NOI18N
}
int lastSeparator = name.lastIndexOf ("/"); // NOI18N
if (lastSeparator > 0) checkLocalFolder (name.substring (0, lastSeparator));
boolean b = f.mkdir();
if (!b) {
throw new IOException(MessageFormat.format (g("EXC_CannotCreateF"), errorParams)); // NOI18N
}
cache.addFolder(name);
}
//-------------------------------------------
/* Create new data file.
*
* @param name name of the file
*
* @return the new data file object
* @exception IOException if the file cannot be created (e.g. already exists)
*/
public void createData (String name) throws IOException {
D.deb("createData("+name+")"); // NOI18N
if( name.startsWith("/") ){ // NOI18N
// Jarda TODO
name=name.substring(1);
D.deb("corrected name='"+name+"'"); // NOI18N
}
File f = getFile (name);
Object[] errorParams = new Object[] {
f.getName (),
getDisplayName (),
f.toString (),
};
int lastSeparator = name.lastIndexOf ("/"); // NOI18N
//if (lastSeparator < 0) lastSeparator = 0;
if (lastSeparator > 0) checkLocalFolder (name.substring (0, lastSeparator));
if (!f.createNewFile ()) {
throw new IOException(MessageFormat.format (g("EXC_DataAlreadyExist"), errorParams)); // NOI18N
}
cache.addFile(name);
cache.setFileStatus(name, cache.localStatusStr);
}
//-------------------------------------------
/* Renames a file.
*
* @param oldName old name of the file
* @param newName new name of the file
*/
public void rename(String oldName, String newName) throws IOException {
D.deb("rename(oldName="+oldName+",newName="+newName+")"); // NOI18N
File of = getFile (oldName);
File nf = getFile (newName);
if (!of.renameTo (nf)) {
throw new IOException(g("EXC_CannotRename", oldName, getDisplayName (), newName)); // NOI18N
}
cache.rename(oldName, newName);
}
//-------------------------------------------
/* Delete the file.
*
* @param name name of file
* @exception IOException if the file could not be deleted
*/
public void delete (String name) throws IOException {
D.deb("delete('"+name+"')"); // NOI18N
File file = getFile (name);
/*
if (!file.delete()) {
throw new IOException (g("EXC_CannotDelete", name, getDisplayName (), file.toString ()));
}
*/
if (!file.exists()) return; // silently ignore non existing files
if (!MiscStuff.deleteRecursive(file)) {
throw new IOException (g("EXC_CannotDelete", name, getDisplayName (), file.toString ())); // NOI18N
}
cache.removeFile(name);
}
//-------------------------------------------
//
// Info
//
//-------------------------------------------
/*
* Get last modification time.
* @param name the file to test
* @return the date
*/
public java.util.Date lastModified(String name) {
D.deb("lastModified("+name+")"); // NOI18N
return new java.util.Date (getFile (name).lastModified ());
}
//-------------------------------------------
/* Test if the file is folder or contains data.
* @param name name of the file
* @return true if the file is folder, false otherwise
*/
public boolean folder (String name) {
return cache.isDir(name);
// return getFile (name).isDirectory ();
}
//-------------------------------------------
/* Test whether this file can be written to or not.
* All folders are not read only, they are created before writting into them.
* @param name the file to test
* @return <CODE>true</CODE> if file is read-only
*/
public boolean readOnly (String name) {
//D.deb("readOnly('"+name+"')"); // NOI18N
if(folder(name)) return false;
return !getFile (name).canWrite ();
}
/** Get the MIME type of the file.
* Uses {@link FileUtil#getMIMEType}.
*
* @param name the file to test
* @return the MIME type textual representation, e.g. <code>"text/plain"</code>
*/
public String mimeType (String name) {
D.deb("mimeType('"+name+"')"); // NOI18N
int i = name.lastIndexOf ('.');
String s;
try {
s = FileUtil.getMIMEType (name.substring (i + 1));
} catch (IndexOutOfBoundsException e) {
s = null;
}
D.deb("mimeType() -> '"+s+"'"); // NOI18N
return s == null ? "content/unknown" : s; // NOI18N
}
//-------------------------------------------
/* Get the size of the file.
*
* @param name the file to test
* @return the size of the file in bytes or zero if the file does not contain data (does not
* exist or is a folder).
*/
public long size (String name) {
D.deb("size("+name+")"); // NOI18N
return getFile (name).length ();
}
/* Get input stream.
*
* @param name the file to test
* @return an input stream to read the contents of this file
* @exception FileNotFoundException if the file does not exists or is invalid
*/
public InputStream inputStream (String name) throws java.io.FileNotFoundException {
//D.deb("inputStream("+name+")"); // NOI18N
return new FileInputStream (getFile (name));
}
//-------------------------------------------
/* Get output stream.
*
* @param name the file to test
* @return output stream to overwrite the contents of this file
* @exception IOException if an error occures (the file is invalid, etc.)
*/
public OutputStream outputStream (String name) throws java.io.IOException {
D.deb("outputStream("+name+")"); // NOI18N
return new FileOutputStream (getFile (name));
}
public synchronized boolean getPromptForLockResult() {
return promptForLockResult;
}
public synchronized void setPromptForLockResult(boolean promptForLockResult) {
this.promptForLockResult = promptForLockResult;
}
/** Run the LOCK command to lock the file.
*
* @param name name of the file
*/
public void lock (String name_) throws IOException {
if (!isImportant(name_)) return; // ignore locking of unimportant files
final String name = name_;
final VcsFileSystem current = this;
new Thread(new Runnable() {
public void run() {
D.deb("lock('"+name+"')"); // NOI18N
D.deb("this = "+this); // NOI18N
File f = getFile (name);
if(f.canWrite ()) return;
Vector files = new Vector();
files.add (name);
getVcsFactory ().getVcsAction (current).doEdit (files);
if(isLockFilesOn ()) {
VcsFile vcsFile = cache.getFile (name);
// *.orig is a temporary file created by AbstractFileObject
// on saving every file to enable undo if saving fails
if(vcsFile==null || vcsFile.isLocal () || name.endsWith (".orig")) return; // NOI18N
else {
D.deb ("lock on file:"+vcsFile.toString()); // NOI18N
setPromptForLockResult(false);
if(isPromptForLockOn ()) {
try {
javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
boolean result;
NotifyDescriptor.Confirmation confirm = new NotifyDescriptor.Confirmation (g("MSG_LockFileCh"), NotifyDescriptor.Confirmation.OK_CANCEL_OPTION); // NOI18N
result = (TopManager.getDefault ().notify (confirm).equals (NotifyDescriptor.Confirmation.OK_OPTION));
setPromptForLockResult(result);
}
});
} catch (InterruptedException e) {
setPromptForLockResult(true);
} catch (java.lang.reflect.InvocationTargetException e) {
setPromptForLockResult(true);
}
}
if(!isPromptForLockOn () || getPromptForLockResult()) {
files = new Vector();
files.add (name);
getVcsFactory ().getVcsAction (current).doLock (files);
}
}
}
}
}, "VCS-Locking Files").start(); // NOI18N
}
/** Does nothing to unlock the file.
*
* @param name name of the file
*/
public void unlock (String name) {
if (!isImportant(name)) return; // ignore unlocking of unimportant files
D.deb("unlock('"+name+"')"); // NOI18N
if(isLockFilesOn ()) {
Vector files = new Vector();
files.add (name);
getVcsFactory ().getVcsAction (this).doUnlock (files);
}
}
//-------------------------------------------
/** Does nothing to mark the file as unimportant.
*
* @param name the file to mark
*
public void markUnimportant (String name) {
// TODO...
D.deb(" ==== markUnimportant("+name+") ==== "); // NOI18N
VcsFile file=cache.getFile(name);
if( file==null ){
//E.err("no such file '"+name+"'"); // NOI18N
return ;
}
file.setImportant(false);
}
*/
public Object getAdvancedConfig () {
return this.advanced;
}
//------------------------------------------
public void setAdvancedConfig (Object advanced) {
//super.setAdvancedConfig (advanced);
this.advanced = advanced;
Vector commands = (Vector) advanced;
int len=commands.size();
commandsByName=new Hashtable(len+5);
for(int i=0;i<len;i++){
UserCommand uc=(UserCommand)commands.elementAt(i);
commandsByName.put(uc.getName(), uc);
}
}
//-------------------------------------------
public Vector getCommands(){
return (Vector) getAdvancedConfig ();
}
//-------------------------------------------
public void setCommands(Vector commands){
setAdvancedConfig (commands);
}
//-------------------------------------------
public UserCommand getCommand(String name){
if( commandsByName==null ){
setCommands ((Vector) getAdvancedConfig ());
}
return (UserCommand)commandsByName.get(name);
}
//-------------------------------------------
public Vector getAdditionalCommands(){
Vector commands=getCommands();
if (commands == null) return null;
int len=commands.size();
Vector additionalCommands=new Vector(5);
for(int i=0;i<len;i++){
UserCommand uc=(UserCommand)commands.elementAt(i);
if( isAdditionalCommand(uc.getName()) ){
additionalCommands.add(uc);
}
}
return additionalCommands;
}
//-------------------------------------------
public boolean isAdditionalCommand(String name){
if( name.equals("LIST") || // NOI18N
name.equals("DETAILS") || // NOI18N
name.equals("CHECKIN") || // NOI18N
name.equals("CHECKOUT") || // NOI18N
name.equals("LOCK") || // NOI18N
name.equals("UNLOCK") || // NOI18N
name.equals("ADD") || // NOI18N
name.equals("REMOVE") || // NOI18N
name.equals("LIST_SUB") ){ // NOI18N
return false ;
}
return true;
}
public FilenameFilter getLocalFileFilter() {
return null;
}
public String getBundleProperty(String s) {
return g(s);
}
//-------------------------------------------
protected String g(String s) {
D.deb("getting "+s);
return NbBundle.getBundle
("org.netbeans.modules.vcs.cmdline.Bundle").getString (s);
}
protected String g(String s, Object obj) {
return MessageFormat.format (g(s), new Object[] { obj });
}
protected String g(String s, Object obj1, Object obj2) {
return MessageFormat.format (g(s), new Object[] { obj1, obj2 });
}
protected String g(String s, Object obj1, Object obj2, Object obj3) {
return MessageFormat.format (g(s), new Object[] { obj1, obj2, obj3 });
}
//-------------------------------------------
}
/*
* Log
* 36 Gandalf-post-FCS1.32.1.2 4/4/00 Martin Entlicher serial version added,
* filesystem always mounts O.K.
* 35 Gandalf-post-FCS1.32.1.1 3/29/00 Martin Entlicher Improved synchronization
* of automatic refresh, support for asking variables, deserialization of
* root fixed.
* 34 Gandalf-post-FCS1.32.1.0 3/23/00 Martin Entlicher Remember the last
* refresh time, remember unimportant names, the change of the status is
* possible to perform recursively, support for asking the user for
* additional variables before the command is run.
* 33 Gandalf 1.32 3/8/00 Martin Entlicher VCS properties read from
* filesystem
* 32 Gandalf 1.31 2/15/00 Martin Entlicher netbeans.user added to
* variables.
* 31 Gandalf 1.30 2/11/00 Martin Entlicher changed setRootDirectory
* to consider its argument as a working directory without module name.
* 30 Gandalf 1.29 2/10/00 Martin Entlicher Locking action changed,
* warning of nonexistent root directory or module name, automatic refresh
* after last command only.
* 29 Gandalf 1.28 2/9/00 Martin Entlicher Set user.home as the
* starting directory.
* 28 Gandalf 1.27 1/19/00 Martin Entlicher Deleted catching of
* annotated name, new files has initial local status.
* 27 Gandalf 1.26 1/18/00 Martin Entlicher
* 26 Gandalf 1.25 1/17/00 Martin Entlicher
* 25 Gandalf 1.24 1/15/00 Ian Formanek NOI18N
* 24 Gandalf 1.23 1/6/00 Martin Entlicher
* 23 Gandalf 1.22 1/5/00 Martin Entlicher
* 22 Gandalf 1.21 12/28/99 Martin Entlicher One ErrorCommandDialog
* for the whole session + Yuri changes
* 21 Gandalf 1.20 12/21/99 Martin Entlicher Refresh time set after
* the filesystem is mounted.
* 20 Gandalf 1.19 12/16/99 Martin Entlicher
* 19 Gandalf 1.18 12/8/99 Martin Entlicher
* 18 Gandalf 1.17 11/30/99 Martin Entlicher
* 17 Gandalf 1.16 11/24/99 Martin Entlicher
* 16 Gandalf 1.15 11/23/99 Martin Entlicher
* 15 Gandalf 1.14 11/16/99 Martin Entlicher Fixed update of file
* status
* 14 Gandalf 1.13 11/9/99 Martin Entlicher
* 13 Gandalf 1.12 11/9/99 Martin Entlicher
* 12 Gandalf 1.11 11/4/99 Martin Entlicher
* 11 Gandalf 1.10 11/2/99 Pavel Buzek statusChanged is using
* fireFileStatusChanged
* 10 Gandalf 1.9 10/26/99 Martin Entlicher
* 9 Gandalf 1.8 10/25/99 Pavel Buzek copyright and log
* 8 Gandalf 1.7 10/23/99 Ian Formanek NO SEMANTIC CHANGE - Sun
* Microsystems Copyright in File Comment
* 7 Gandalf 1.6 10/12/99 Pavel Buzek
* 6 Gandalf 1.5 10/9/99 Pavel Buzek
* 5 Gandalf 1.4 10/9/99 Pavel Buzek
* 4 Gandalf 1.3 10/9/99 Pavel Buzek
* 3 Gandalf 1.2 10/7/99 Pavel Buzek
* 2 Gandalf 1.1 10/5/99 Pavel Buzek VCS at least can be
* mounted
* 1 Gandalf 1.0 9/30/99 Pavel Buzek
* $
*/